Skip to content

Your activity

Not opened yet on this device.

1. Design of Metamaterials using Topology Optimization

Property-based topology optimization designs a microstructure whose homogenized stiffness matches a target tensor. Use it to synthesize metamaterial unit cells with SIMP, filters and sensitivity weighting, then to verify the result with a full-scale finite-element model.

Reading time
9 min
Length
1,577 words
Updated
Sep 15, 2026
Total views
--

1. Topology Optimization using the target mechanical property โ€‹

There are two main reference for the property-based topology optimization, including [1] and [2]

We already know the homogenization issue and the energy-based homogenization from [3] and [4]. So we start from the Section4 of [1:1], which is the optimization model.

(1) Optimization Model โ€‹

We use the SIMP approach for the design of metamaterials, the density is defined as :

(1.1.1)Ee(ฯe)=Emin+ฯp(E0โˆ’Emin)

The Formulation of optimization should be :

(1.1.2)minฯ:c(EijklH)(ฯ)k,l=1,โ€ฆ,ds.t.:KUA(kl)=F(kl)โˆ‘e=1Nveฯe|Y|โ‰คฮธ0โ‰คฯeโ‰ค1

Here, c is an extractor function for the EijklH. Where for 2D case, i,j,k,l=1,2.

(2) FE Analysis Process โ€‹

Firstly, to check how periodic boundary conditions are applied and solved, for example MATLAB code of it, see [5] for details.

For the FE analysis, we can use follows :

(1.2.1)[K11K12โ€ฆK14โ€ฆโ€ฆK44][Uโ€•1U2U3U4]=[F1F2F3F4]

We note F1 is unknown, F2=0, F3+F4=0, then U4=U3+Wโ€• and K is symmetric

Reduction to the 2ร—2 system (Matrix reduction technique) โ€‹

The prescribed displacement and periodic relation leave U2 and U3 as the independent unknowns. We should define the unknown vector q=[U2,U3]T, and split form of U

(1.2.2)q=[U2U3],U0=[Uโ€•100Wโ€•],T=[00100101].

Therefore, the complete displacement vector is :

(1.2.3)U=U0+Tq.

Premultiplication by TT removes the unknown reaction F1 and combines the two periodic-boundary equations into F3+F4=0 :

(1.2.4)TTKTq=โˆ’TTKU0.

Using the symmetry of K gives the explicit reduced system

(1.2.5)[K22K23+K24K23+K24K33+2K34+K44][U2U3]=โˆ’[K12Uโ€•1+K24Wโ€•(K13+K14)Uโ€•1+(K34+K44)Wโ€•].

After solving this system, recover the slave-boundary displacement and the reaction by

(1.2.6)U4=U3+Wโ€•,F1=(KU)1.

Matrix Reduction
For a general matrix with some known variables, we can use following steps to reduce it to a lower-rank problem :

Ax=b

we don't want b to appear in the final form.

  1. If the x is partially known, express the x as :
x=x0+Tq

where q is unknown vectors

  1. Then we need another matrix P
PTb=0โ†’PTK(x0+Tq)=0

the new form is reached.

This transformation is also directly applicable in symbolic software such as SymPy, Mathematica, or the MATLAB Symbolic Math Toolbox. In an FE solver, the same relation is generally imposed with a multi-point constraint (MPC).

matlab
%%%%%%%%%%%%%%%%%% FE-ANALYSIS  %%%%%%%%%%%%%%
sK = reshape(KE(:)*(Emin+xPhys(:)'.^penal*(E0-Emin)),64*nelx*nely,1);
K = sparse(iK,jK,sK); K = (K+K')/2;
Kr = [K(d2,d2), K(d2,d3)+K(d2,d4); K(d3,d2)+K(d4,d2), K(d3,d3)+K(d4,d3)+K(d3,d4)+K(d4,d4)];
U(d1,:) = ufixed;
U([d2,d3],:) = Kr\(-[K(d2,d1); K(d3,d1)+K(d4,d1)]*ufixed-[K(d2,d4); K(d3,d4)+K(d4,d4)]*wfixed);
U(d4,:) = U(d3,:)+wfixed;

(3) Derivatives of Homogenized Stiffness matrix โ€‹

We compute the derivation against every element density ฯe as :

(1.3.1)โˆ‚EijklHโˆ‚ฯe=1|Y|pฯepโˆ’1

From [2:1], we know that we have (1.1.1)[3:1], then :

(1.3.2)EijklH=1|Y|โˆ‘e=1NE(ue0โˆ’ueโˆ—(ij))ke(ue0โˆ’ueโˆ—(kl))

Then we have ke=ฯke0, then the derivation is :

(1.3.3)โˆ‚EijklHโˆ‚ฯe=1Ypฯpโˆ’1(ue0โˆ’ueโˆ—(ij))ke(ue0โˆ’ueโˆ—(kl))

To compute above expression in dolfinx, it's an anisotropic computation, the stress of it should be computed by C66. We note that in the computation of homogenization, we have already solved ueij, so we can use mat.mu and mat.lmbda to compute the stress :

(1.3.4)ฯต=ฯตฯƒ+ฯตth+ฯตv

(4) Application cases for the design of meta-materials โ€‹

For example, we want to maximize the bulk modulus[6] in (1.7)[6:1], we will minimize the negative of it :

(1.4.1)c(Eijkl)=โˆ’4KฮตH(ฯ)โŸถ2Dโˆ’(E1111+E1122+E2211+E2222)

Or the shear modulus in (2.4)[6:2], it is :

(1.4.2)c(Eijkl)=โˆ’EijijโŸถ2Dโˆ’E1212

the i,j ==depends on the direction it wants to minimize==.

We also note besides (1.4.1~2), the article[2:2] gives a more general form, reads :

(1.4.3)Maximize :J(u,ฮฆ)=โˆ‘i,j,k,l=1dฮทijklEijklH(ฯ)

where ฮทijkl denotes the coefficient of each term.

If we want to reach a material with negative-poission's ratio, we want to minimize the Poisson's ratio ฮฝ , Using (2.3)[7] and Fig. 2.2[7:1], we have :

(1.4.4)ฮฝ=E1122E1111+E1122

The above expression is not the general Poisson ratio of a homogenized 2D cell. It recovers the original 3D isotropic Poisson ratio only when (C) is an isotropic plane-strain stiffness tensor.

Since the optimized microstructure is generally anisotropic, so that conversion no longer applies.

The definition under uniaxial loading in direction 1 is :

(1.4.5)ฮฝ12โˆ’ฮต22ฮต11whenฯƒ22=ฯƒ12=0

We let the homogenized compliance as :

(1.4.6)SH=(CH)โˆ’1

Then the general expression of Poisson's ratio are :

(1.4.7)ฮฝ12=โˆ’S2211HS1111H

and :

(1.4.8)ฮฝ21=โˆ’S1122HS2222H

To get the poisson's ratio, we can use :

python
C_sym = 0.5 * (C_H + C_H.T)
S_H = np.linalg.inv(C_sym)

In the original essay, the target function is set as:

(1.4.9)c=E1122โˆ’ฮฒl(E1111+E2222)

where l is the loop number, which finally transfer the objective to elastic tensor E1122. And also, to prevent the OC issue, the essay modified the OC bisection rule as :

matlab
xnew = max(0,max(x-move,min(1, min(x+move,x.*(-dc./dv/lmid)))));

Why setting this target function reasonable ? Under uniaxial stress in direction 1, we have :

S=Cโˆ—|C|โ†’v12โ†’C1122C2222

To maximize C1122 while minimize C2222 and C1111, the target c is reasonable.

2. Implementation by FEniCSx โ€‹

(1) Indexing rule of Sphinx โ€‹

  1. In the dolfinx, every component has a ufl_shape attribute, so they have multiple components, We can access the components directly by indexing. For example, 2d strain/stress ฮตij is (2x2) matrix and 3d strain is (3x3) matrix.
python
u_sol[0]   # x displacement  
u_sol[1]   # y displacement 

# they returns a Indexed object 
# For example, to compute the magnitude in another space, we can use interpolate + expression : 
V_mag = fem.functionspace(msh, ("CG", 1))
u_magnitude = fem.Function(V_mag, name="displacement_magnitude")
u_magnitude.interpolate(
    fem.Expression(
        ufl.sqrt(u_sol[0] ** 2 + u_sol[1] ** 2),  # also ufl.inner(u_sol, u_sol)
        V_mag.element.interpolation_points,
    )
)
u_magnitude.x.scatter_forward()
plot_function_pv(u_magnitude, title="displacement magnitude")

(2) Stress Computation for anisotropic materials โ€‹

So, having the C66 matrix CH, we also need the voigt strain form, which is :

(2.2.1)[ฮต11ฮต22ฮต12]T(2D)[ฮต11ฮต22ฮต22ฮต12ฮต13ฮต23]T(3D)

In implementation, we firstly, need two transfer functions to transfer the tensor notation to voigt notation, as given in [8], then, we can implement the stress computation as :

python
def stress_e_c66 (C66, eps):
	eps_voigt = tensor_to_voigt(eps, engineering_shear=True)
	C_const = ufl.as_matrix(C) 
	sigma_voigt = ufl.dot(
ย  ย  ย  ย  C_ufl,
	ย  ย  eps_voigt,
ย  ย  )
	return voigt_to_tensor(sigma_voigt, dim)

(3) Computation of Derivatives of Stiffness Matrix โ€‹

We know from (1.3.3) that, we can compute the derivatives by :

(2.3.1)โˆ‚EijklHโˆ‚ฯe=pฯpโˆ’1(ฮตe0โˆ’ฮตeij)C(ฮตe0โˆ’ฮตekl)

For sensitivity of stiffness matrix, we have following computations :

python
def effective_stiffness_sensitivity(
    homo: CellHomogenization,
    rho: np.ndarray,
) -> tuple[np.ndarray, np.ndarray]:
    r"""Assemble the elementwise derivatives of the homogenized stiffness.

    .. math::
        \frac{\partial C^H_{IJ}}{\partial \rho_e}
        = \frac{1}{\lvert Y \rvert} \int_{Y_e} \eta^{(I)} : \mathbb{C}_{,\rho} : \eta^{(J)} \, dY.

    where :math:`I = ij,  J = kl`

    The EBHM correctors use
    :math:`\eta^{(I)} = E^{(I)} - \varepsilon(\chi^{(I)})`.
    """
    homo.update_density(rho)
    mat, msh = homo.mat, homo.msh
    V0 = fem.functionspace(msh, ("DG", 0))
    C_H, correctors = homo.solve_ebhm(return_u_solu=True)
    dim = homo.dim
    comps = len(unit_strain_tensors(dim=dim))
    if len(correctors) != comps:
        raise ValueError("Number of correctors must match the Voigt components.")

    unit_strains = unit_strain_tensors(dim=dim)
    eta = [
        fem.Constant(msh, unit_strains[I]) - strain(correctors[I])
        for I in range(comps)
    ]
    unit_vec = ufl.TestFunction(V0)
    dC_H_drho = np.zeros((comps, comps, len(rho)))

    dscale = (
        homo.penalty
        * (homo.rho_max - homo.rho_min)
        * rho ** (homo.penalty - 1.0)
    )
    for I, eta_I in enumerate(eta):
        for J, eta_J in enumerate(eta):
            Q = ufl.inner(stress_e(mat.mu, mat.lmbda, eta_I), eta_J)
            Q_vec = fem.assemble_vector(
                fem.form(Q * unit_vec * ufl.dx)
            )
            dC_H_drho[I, J, :] = dscale * Q_vec.array / homo.vol
    return C_H, dC_H_drho

We note here we use the bulk modulus in (1.4.1) as the objective, After getting the derivative, we can plot and check the components, the [0, 0] component of the gradient โˆ‚Cโˆ‚ฯ is plotted as follows :

2.1

while the [0, 1] component is like :

2.2

(4) Essential problems โ€‹

1) Filter Configurations โ€‹

Firstly we have, for (2.3.1) :

(2.4.1)โˆ‚CIJHโˆ‚ฯe=1|Y|โˆซYep(ฯmaxโˆ’ฯmin)ฯepโˆ’1ฮท(I):C0:ฮท(J)dY

For the density filtering, we only use the density filtering only to get the final physical density, if we apply the filter in the middle process, the filter may hinder the density transformation process, or even deteriorate the results.

If we use the physical (filtered) density to compute the dJdฯ, the result is โˆ‚Jโˆ‚ฯp. so we have ฮดฯp=Hฮดฯd where p means physical and d means design :

(2.4.2)ฮดJ=(โˆ‚Jโˆ‚ฯp)Tฮดฯp=(โˆ‚Jโˆ‚ฯp)THโŸfiltered densityย ฮดฯd

We have following result :

(2.4.3)โˆ‚Jโˆ‚ฯ|filtered=HTโˆ‚Jโˆ‚ฯp

In the code example, we can implement this filtering process by following :

python
density_filter = DistanceBasedFilter(  
    msh,  
    rmin=filter_radius * min_mesh_size(msh),  
    p=1.0,  
    filter_type="element",  
)
C_H, dC_H_drho = effective_stiffness_sensitivity(
	homo,
	rho_physical,
)
# C^H_1111 + C^H_1122 + C^H_2211 + C^H_2222
bulk_stiffness = float(np.sum(C_H[:2, :2]))  # float(C_H[0, 1])  
dJ_drho = -np.sum(dC_H_drho[:2, :2, :], axis=(0, 1))  # - dC_H_drho[0, 1, :]    
dJ_drho_filtered = density_filter.apply_adjoint(dJ_drho)
vols_filtered = density_filter.apply_adjoint(vols)
Lmbda = optimization_criteria(
	rho=rho_design,
	dc_drho=dJ_drho_filtered,
	elem_vols=vols_filtered,
	vol_frac=volume_fraction,
	zeta=move_limit,
	precision=1e-6,
	lmbda_range=(1e-30, 1e30),
	rho_min=rho_min,
	rho_max=rho_max,
)

In the essay that we references, the article provides 2 types of filters. In our cellgen package, the DistanceBasedFilter applying at both sensitivity and volume is same to filter2. According to the article, there are 2 types of filters, given in the following code :

matlab
%% FILTERING/MODIFICATION OF SENSITIVITIES
if ft == 1
dc(:) = H*(x(:).*dc(:))./Hs./max(1e-3,x(:));
elseif ft == 2
dc(:) = H*(dc(:)./Hs);
dv(:) = H*(dv(:)./Hs);
end
โˆ‚Jโˆ‚ฯ|filtered=1ฯHT(ฯโˆ‚Jโˆ‚ฯ)

2.3

2) The crucial Initial Condition Problem โ€‹

In the debugging process, one of the critical problem is the initial condition. This can simply be fixed by modifying the line as :

matlab
if sqrt((i-nelx/2-0.5)^2+(j-nely/2-0.5)^2) < min(nelx,nely)/3 
%%%%%%%%%%%% set to follows %%%%%%%%%%%%
if sqrt((i-nelx/2-0.5)^2+(j-nely/2-0.5)^2) < 0.15 * nelx

2.4

3) MMA problem for inner density increase โ€‹

The unconservative MMA updates, while topX.m uses density-filtered OC bisection. MMA continued making large moves after the objective peaked, transferring density into the center. So, the OC optimizer is generally stabler than MMA optimizer.

We note that lower penalty is also beneficial. penalty = 3.5 -> 4 is a better choice than the penalty = 5.

One of the problem is, the filter radius is also crucial. A good choice is 1.3ย โˆผย 1.5. When the filter radius is set to a larger value like 2, the result can be asymmetric. The following structure is reached by setting r=1.5 .

2.5

The following gives a result on r=2 :

2.6

(5) Poisson's ratio โ€‹

By the optimization of Poisson's ratio, we can still reach some materials with negative Poisson's ratio.

The generation configs is center hole radius 1/6, with following configs :

python
volume_fraction = 0.3
rho_min = 1e-9

config = UnitCellOptimProblemConfig(
	vol_frac=volume_fraction,
	max_iterations=150,
	result_dir=str(Path(__file__).parent),
	result_filename="microcell_top_design_density.xdmf",
	density_filename="microcell_top_design_best_density.npy",
	penalty=3.5,
	filter_radius=1.2,
	rho_min=rho_min,
	rho_max=1.0,
	move_limit=0.1,
	backend="mma",  # Use "oc" to select the optimality-criteria update.
	mma_auxiliary_penalty=1e5,
	mma_max_subproblem_iter=200,
)

Under these configurations, the poission's ratio is -0.5, resulting microstructure is given in Fig. 2.7 :

2.7

We can validate the auxetic properties by ANSYS using a lattice constructed by this cell :

2.8

Another typical configuration is:

python
def center_hole(x):
    return (x[0, :] - 0.5) ** 2 + (x[1, :] - 0.5) ** 2 < ((0.2) ** 2)

volume_fraction = 0.4
rho_min = 1e-9

config = UnitCellOptimProblemConfig(
	vol_frac=volume_fraction,
	max_iterations=150,
	result_dir=str(Path(__file__).parent),
	result_filename="microcell_top_design_density.xdmf",
	density_filename="microcell_top_design_best_density.npy",
	penalty=3.5,
	filter_p=0.7,
	filter_radius=1.0,
	rho_min=rho_min,
	rho_max=1.0,
	move_limit=0.1,
	backend="mma",  # Use "oc" to select the optimality-criteria update.
	mma_auxiliary_penalty=1e5,
	mma_max_subproblem_iter=200,
)

The optimization result under 50x50 resolutions are given in Fig. 2.9

2.9

2.10

We note fhat we may need to increase the resolution into 200x200 for a detailed structure, We can get the structure with more detailed structure layout, given by following picture :

2.11


  1. Xia, Liang, and Piotr Breitkopf. โ€œDesign of Materials Using Topology Optimization and Energy-Based Homogenization Approach in Matlab.โ€ Structural and Multidisciplinary Optimization 52, no. 6 (2015): 1229โ€“41. https://doi.org/10.1007/s00158-015-1294-0. โ†ฉ๏ธŽ โ†ฉ๏ธŽ

  2. Gao, Jie, Hao Li, Zhen Luo, Liang Gao, and Al Et. โ€œTopology Optimization of Micro-Structured Materials Featured with the Specific Mechanical Properties.โ€ International Journal of Computational Methods, ahead of print, 2019. https://doi.org/10.1142/S021987621850144X. โ†ฉ๏ธŽ โ†ฉ๏ธŽ โ†ฉ๏ธŽ

  3. Second Form of Equivalent Stiffness โ†ฉ๏ธŽ โ†ฉ๏ธŽ

  4. EBHM โ†ฉ๏ธŽ

  5. PBC โ†ฉ๏ธŽ

  6. 3. Definition of Common Mechanical Modulus โ†ฉ๏ธŽ โ†ฉ๏ธŽ โ†ฉ๏ธŽ

  7. Hook's Law โ†ฉ๏ธŽ โ†ฉ๏ธŽ

  8. 1. Symmetric Stiffness Tensor and Voigt notation โ†ฉ๏ธŽ